//
//  main.m
//  SchemaToClass
//
//  Created by Chenly on 14-1-20.
//  Copyright (c) 2014年 Chenly. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "RXMLElement.h"
#import "Bean.h"
#import "BeansManager.h"

typedef NS_ENUM(NSUInteger, BeanElementType) {
    BeanElementTypeElement,
    BeanElementTypeComplexType
};

void exitForWrongArgs(NSString *error)
{
    NSLog(@"Error: %@", error);
    exit(1);
}

static NSString *sNamespace = @"http://www.w3.org/2001/XMLSchema";
static NSString *sFileName;
static NSMutableDictionary *sNamespaceDictionary;
static NSMutableArray *externClasses;

NSString* getPrefixOfNamespace(NSString *namespace, NSString *fileContent)
{
    NSString *prefix;
    
    NSInteger namespaceLocation = [fileContent rangeOfString:namespace].location;
    NSString *searchedContent = [fileContent substringToIndex:namespaceLocation];
    NSInteger xmlnsIndex = [searchedContent rangeOfString:@"xmlns" options:NSBackwardsSearch].location;
    prefix = [searchedContent substringFromIndex:xmlnsIndex];
    prefix = [prefix substringFromIndex:([prefix rangeOfString:@":"].location + 1)];
    prefix = [prefix stringByReplacingOccurrencesOfString:@"=" withString:@""];
    prefix = [prefix stringByReplacingOccurrencesOfString:@"\"" withString:@""];
    prefix = [prefix stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
    
    return prefix;
}

Bean *parseBeanFromXMLElement(RXMLElement *beanElement, BeanElementType type) {

    Bean *bean;
    
    if (type == BeanElementTypeElement) {
        
        RXMLElement *complexElement = [beanElement child:@"complexType" inNamespace:sNamespace];
        if (complexElement) {
            
            bean =  parseBeanFromXMLElement(complexElement, BeanElementTypeComplexType);
        }
        else
            bean = [[Bean alloc] init];
    }
    else
    {
        bean = [[Bean alloc] init];
        
        NSMutableDictionary *properties = [NSMutableDictionary dictionary];
        NSMutableDictionary *documentations = [NSMutableDictionary dictionary];
        NSMutableDictionary *arrayPropertyTypes = [NSMutableDictionary dictionary];
        NSMutableArray *arrayInnerBeans = [NSMutableArray array];
        
        NSMutableArray *elements = [[NSMutableArray alloc] init];
        [elements addObjectsFromArray:[[beanElement child:@"all" inNamespace:sNamespace] children:@"element" inNamespace:sNamespace]];
        [elements addObjectsFromArray:[[beanElement child:@"sequence" inNamespace:sNamespace] children:@"element" inNamespace:sNamespace]];
        [elements addObjectsFromArray:[[beanElement child:@"any" inNamespace:sNamespace] children:@"element" inNamespace:sNamespace]];
        [elements addObjectsFromArray:[[beanElement child:@"choice" inNamespace:sNamespace] children:@"element" inNamespace:sNamespace]];
        [elements addObjectsFromArray:[beanElement children:@"attribute" inNamespace:sNamespace]];
        
        for (RXMLElement *property in elements) {
            
            NSString *propertyName = [property attribute:@"name"];
            NSString *propertyType = [property attribute:@"type"];
            
            if (propertyName == nil) {
                
                propertyName = [property attribute:@"ref"];
                propertyType = propertyName;
                if (propertyName == nil) {
                    
                    continue;
                }
            }
            
            NSArray *components = [propertyName componentsSeparatedByString:@"_"];
            NSMutableString *newProperty = [[NSMutableString alloc] init];
            
            for(int i = 0; i < components.count; i ++)
            {
                NSString *component = components[i];
                if (![component isEqualToString:@""]) {
                    
                    if (i == 0) {
                        
                        for (int j = 0; j < component.length; j ++) {
                            
                            if (j == component.length - 1) {
                                
                                component = [component stringByReplacingCharactersInRange:NSMakeRange(j, 1) withString:[[component substringWithRange:NSMakeRange(j, 1)] lowercaseString]];
                                break;
                            }
                            
                            unichar character = [component characterAtIndex:j + 1];
                            if (character >= 'a' && character <= 'z') {
                                
                                break;
                            }
                            component = [component stringByReplacingCharactersInRange:NSMakeRange(j, 1) withString:[[component substringWithRange:NSMakeRange(j, 1)] lowercaseString]];
                        }
                        component = [component stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:[[component substringToIndex:1] lowercaseString]];
                    }
                    else
                    {
                        component = [component stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:[[component substringToIndex:1] uppercaseString]];
                    }
                    [newProperty appendString:component];
                }
                if (i < components.count - 1) {
                    
                    [newProperty appendString:@"_"];
                }
            }
            propertyName = newProperty;
            if ([propertyName isEqualToString:@"id"]) {
                
                propertyName = @"_id";
            }
            if ([propertyName isEqualToString:@"class"]) {
                
                propertyName = @"_class";
            }
            if ([propertyName isEqualToString:@"return"]) {
                
                propertyName = @"_return";
            }
            
            if (!propertyType) {
                
                propertyType = [[[property child:@"simpleType"] child:@"restriction"] attribute:@"base"];
            }
            
            if (propertyType) {
                
                NSInteger subIndex = [propertyType rangeOfString:@":"].location;
                if (subIndex != NSNotFound) {
                    
                    NSString *namespacePrefix = [propertyType substringToIndex:subIndex];
                    namespacePrefix = [namespacePrefix stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
                    propertyType = [propertyType substringFromIndex:subIndex + 1];
                    
                    if (![namespacePrefix isEqualToString:@"xs"]) {

                        propertyType = [[NSString stringWithFormat:@"%@_%@", sNamespaceDictionary[namespacePrefix], propertyType] capitalizedString];
                        [externClasses addObject:propertyType];
                    }
                }
                else
                {
                    propertyType = [[NSString stringWithFormat:@"%@_%@", sFileName, propertyType] capitalizedString];
                }
            }
            else
            {
                static int innerNumber = 0;
                propertyType = [[NSString stringWithFormat:@"%@_Inner%@", sFileName, innerNumber == 0 ? @"" : [[NSNumber numberWithInt:innerNumber] stringValue]] capitalizedString];
                innerNumber ++;
                Bean *innerBean = parseBeanFromXMLElement(property, BeanElementTypeElement);
                innerBean.name = propertyType;
                [arrayInnerBeans addObject:innerBean];
                
            }
            
            if ([property attribute:@"maxOccurs"]) {
                
                [properties setObject:@"array" forKey:propertyName];
                [arrayPropertyTypes setObject:propertyType forKey:propertyName];
            }
            else
            {
                [properties setObject:propertyType forKey:propertyName];
            }
            
            RXMLElement *documentation = [property child:@"annotation.documentation"];
            [documentations setObject:documentation.text == nil ? @"" : documentation.text forKey:propertyName];
        }
        
        bean.properties = properties;
        bean.documentations = documentations;
        bean.arrayPropertyTypes = arrayPropertyTypes;
        bean.innerBeans = arrayInnerBeans;
    }
    
    bean.name = [NSString stringWithFormat:@"%@_%@", sFileName, [beanElement attribute:@"name"]];
    
    RXMLElement *documentation = [beanElement child:@"annotation.documentation" inNamespace:sNamespace];
    bean.document = documentation.text;
    if (bean.document == nil) {
        
        bean.document = @"";
    }
    
    return bean;
}

int main(int argc, const char * argv[])
{
    @autoreleasepool {
        
        if (argc < 2) {
            
            exitForWrongArgs(@"缺少参数！");
        }
        else if(argc > 3)
        {
            exitForWrongArgs(@"多余参数！");
        }
        
        NSString *rootPath = [NSString stringWithUTF8String:argv[0]];
        rootPath = [rootPath stringByDeletingLastPathComponent];
        NSString *filePath = nil;
        NSString *outputPath = nil;
        
        for (int i = 1; i < argc; i ++) {
            
            NSString *argStr = [NSString stringWithUTF8String:argv[i]];
            
            if ([argStr rangeOfString:@"-out="].location == NSNotFound) {
                
                if (filePath) {
                    
                    exitForWrongArgs(@"参数不正确, 检查参数格式");
                }
                else
                {
                    if ([argStr rangeOfString:@"/"].location == 0) {
                        
                        filePath = argStr;
                    }
                    else if ([argStr rangeOfString:@".."].location == 0) {
                        
                        filePath = [[rootPath stringByDeletingLastPathComponent] stringByAppendingPathComponent:[argStr substringFromIndex:3]];
                    }
                    else if ([argStr rangeOfString:@"."].location == 0) {
                        
                        filePath = [rootPath stringByAppendingPathComponent:[argStr substringFromIndex:2]];
                    }
                    else
                    {
                        filePath = [rootPath stringByAppendingPathComponent:argStr];
                    }
                }
            }
            else
            {
                if (outputPath) {
                    
                    exitForWrongArgs(@"参数不正确, 检查是否存在两个-out");
                }
                else
                {
                    argStr = [argStr substringFromIndex:5];
                    
                    if ([argStr rangeOfString:@"/"].location == 0) {
                        
                        outputPath = argStr;
                    }
                    else if ([argStr rangeOfString:@".."].location == 0) {
                        
                        outputPath = [[rootPath stringByDeletingLastPathComponent] stringByAppendingPathComponent:[argStr substringFromIndex:3]];
                    }
                    else if ([argStr rangeOfString:@"."].location == 0) {
                        
                        outputPath = [rootPath stringByAppendingPathComponent:[argStr substringFromIndex:2]];
                    }
                    else
                    {
                        outputPath = [rootPath stringByAppendingPathComponent:argStr];
                    }
                }
            }
        }
        
        if (!outputPath) {
            
            outputPath = rootPath;
        }
        BOOL filePathIsDirectory;
        BOOL outputPathIsDirectory;
        
        if (![[NSFileManager defaultManager] fileExistsAtPath:filePath isDirectory:&filePathIsDirectory] || filePathIsDirectory) {

            exitForWrongArgs(@"schema文件不存在");
        }

        if (![[NSFileManager defaultManager] fileExistsAtPath:outputPath isDirectory:&outputPathIsDirectory] || !outputPathIsDirectory) {
            
            exitForWrongArgs(@"文件输出目录错误");
        }
        
        sFileName = [[filePath lastPathComponent] stringByDeletingPathExtension];

        NSMutableArray *beans = [NSMutableArray array];
        NSMutableArray *importFiles = [NSMutableArray array];

        sNamespaceDictionary = [NSMutableDictionary dictionary];
        externClasses = [NSMutableArray array];
        
        RXMLElement *root = [RXMLElement elementFromXMLFilePath:filePath];
        //需要引入的包
        NSString *fileContent = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
        fileContent = [fileContent substringFromIndex:[fileContent rangeOfString:@"<xs:schema"].location];
        fileContent = [fileContent substringToIndex:[fileContent rangeOfString:@">"].location];

        for (RXMLElement *importElement in [root children:@"import" inNamespace:sNamespace]) {
            
            NSString *namespace = [importElement attribute:@"namespace"];
            NSString *fileName = [[[importElement attribute:@"schemaLocation"] lastPathComponent] stringByDeletingPathExtension];
            [importFiles addObject:fileName];
            [sNamespaceDictionary setObject:fileName forKey:getPrefixOfNamespace(namespace, fileContent)];
        }

        for (RXMLElement *element in [root children:@"element" inNamespace:sNamespace]) {
            
            Bean *bean = parseBeanFromXMLElement(element, BeanElementTypeElement);
            if (bean) {
                
                BOOL flag = YES;
                for (Bean *complexBean in beans) {
                    
                    if ([complexBean.name isEqualToString:bean.name]) {
                        
                        if (complexBean.document.length == 0) {
                            
                            complexBean.document = bean.document;
                        }
                        flag = NO;
                        break;
                    }
                }
                if (flag) {
                    
                    [beans addObject:bean];
                }
            }
        }
        
        for (RXMLElement *complexType in [root children:@"complexType" inNamespace:sNamespace]) {
            
            Bean *bean = parseBeanFromXMLElement(complexType, BeanElementTypeComplexType);
            
            BOOL flag = YES;
            for (Bean *elementBean in beans) {
                
                if ([elementBean.name isEqualToString:bean.name]) {
                    
                    if (bean.document.length == 0) {
                        
                        bean.document = elementBean.document;
                    }
                    bean = elementBean;
                    flag = NO;
                    break;
                }
            }
            if (flag) {
                
                [beans addObject:bean];
            }
        }
        
        BeansManager *manager = [[BeansManager alloc] init];
        [manager writeBeans:beans externClasses:externClasses importFiles:importFiles toDirectory:outputPath fileName:sFileName];
    }
    
    return 0;
}

